package gui;

import client.ConnectionStatus;
import client.MirrorLocalOnRemoteTask;
import client.MirrorRemoteOnLocalTask;
import client.SyncClient;
import etc.SynchronizationParameters;
import etc.Utils;
import java.io.File;
import java.util.ArrayList;
import java.util.Date;
import java.util.Observable;
import java.util.Observer;

public class ClientController extends Observable implements Observer {

	private ArrayList<File> localFileList = null;

	private ArrayList<String[]> remoteFilesTableView = null, localFilesTableView = null;

	private ArrayList<String[]> filesToGet = null, filesToPut = null, localNewFiles = null, remoteNewFiles = null;
	
	private SyncClient client;

	private Thread syncThread = null;

	private Integer syncTaskCount = 0;

	private String syncLog = new String();

	public ClientController() {
		client = new SyncClient();
	}

	public ConnectionStatus startClient() {
		if( connect() ) {
			if( login() ) {
				getAndCompareFiles();
				return ConnectionStatus.AUTHORIZED;
			}
			return ConnectionStatus.CONNECTED;
		}

		return ConnectionStatus.DISCONNECTED;
	}

	public void stopClient() {
		logout();
		client.disconnect();

		cleanTables();
	}

	public void mirrorRemoteOnLocal() {
		syncLog = new String();
		
		syncTaskCount = filesToGet.size() + localNewFiles.size();

		MirrorRemoteOnLocalTask syncTask = new MirrorRemoteOnLocalTask(client, filesToGet, localNewFiles);
		syncTask.addObserver(this);

		syncThread = new Thread( syncTask );
		syncThread.start();
	}

	public void mirrorLocalOnRemote() {
		syncLog = new String();
		
		syncTaskCount = filesToPut.size() + remoteNewFiles.size();

		MirrorLocalOnRemoteTask syncTask = new MirrorLocalOnRemoteTask(client, filesToPut, remoteNewFiles);
		syncTask.addObserver(this);

		syncThread = new Thread( syncTask );
		syncThread.start();
	}

	private Boolean connect() {
		String notifyMsg = null;

		if( client == null ) {
			notifyMsg = "Cannot connect, client object is not yet available";
		}else{
			notifyMsg = client.connect();
		}

		setChanged();
		notifyObservers(notifyMsg);

		return( client.isConnected() );
	}

	private Boolean login() {
		String notifyMsg = null;

		if( client == null ) {
			notifyMsg = "Cannot login, client object is not yet available";
			setChanged();
			notifyObservers(notifyMsg);

			return false;
		}else{
			if( client.isConnected() ) {
				notifyMsg = client.login();
				setChanged();
				notifyObservers(notifyMsg);

				return notifyMsg.contains("Authorized");
			}else{
				notifyMsg = "Cannot login, client is not connected to any host.";
				setChanged();
				notifyObservers(notifyMsg);

				return false;
			}
		}
	}

	private Boolean logout() {
		if( syncThread != null ) {
			syncThread.interrupt();
			syncThread = null;
		}
		
		String notifyMsg = null;

		if( client == null ) {
			notifyMsg = "Cannot logout, client object is not yet available";
			setChanged();
			notifyObservers(notifyMsg);

			return false;
		}else{
			if( client.isConnected() ) {
				notifyMsg = client.logout();
				setChanged();
				notifyObservers(notifyMsg);

				return true;
			}else{
				notifyMsg = "Cannot logout, client is not connected to any host.";
				setChanged();
				notifyObservers(notifyMsg);

				return false;
			}
		}
	}

	private void cleanTables() {
		ArrayList<String[]> temp = new ArrayList<String[]>();
		String[] tempArr = new String[]{ "local" };
		temp.add(tempArr);
		setChanged();
		notifyObservers( temp );

		ArrayList<String[]> temp2 = new ArrayList<String[]>();
		String[] tempArr2 = new String[]{ "remote" };
		temp2.add(tempArr2);
		setChanged();
		notifyObservers( temp2 );
	}
	
	public void getAndCompareFiles() {
		cleanTables();
		
		if( (client != null) && (client.isConnected()) ) {
			remoteFilesTableView = client.requestRemoteFileList();

			if( remoteFilesTableView == null ) {
				String clientStatus = "Could not get remote file list.";
				setChanged();
				notifyObservers( clientStatus );
				return;
			}

			filesToPut = new ArrayList<String[]>();
			filesToGet = new ArrayList<String[]>();
			remoteNewFiles = new ArrayList<String[]>();
			localNewFiles = new ArrayList<String[]>();
			localFilesTableView = new ArrayList<String[]>();

			localFileList = Utils.listFilesInDir( SynchronizationParameters.getLocalPath() );
			File rootFolder = new File( SynchronizationParameters.getLocalPath() );
			for (File file : localFileList) {
				String localFileName = rootFolder.toURI().relativize( file.toURI() ).getPath();
				String localFileLength = Long.toString( file.length() );
				String localFileLastModified = new Date( file.lastModified() ).toString();

			 	Boolean found = false;
				for (String[] row : remoteFilesTableView) {
					if( row[0].equalsIgnoreCase( localFileName ) ) {
						if( row[2].equalsIgnoreCase( localFileLength )
							&& row[3].equalsIgnoreCase( localFileLastModified ) ) {
							localFilesTableView.add( new String[]{ localFileName, "SAME", localFileLength, localFileLastModified } );
							row[1] = "SAME";
						}else{
							localFilesTableView.add( new String[]{ localFileName, "MODIFIED", localFileLength, localFileLastModified } );
							row[1] = "MODIFIED";
							filesToPut.add( new String[]{ localFileName, localFileLength, localFileLastModified } );
						}
						found = true;
						break;
					}
				}

				if( !found ) {
					localFilesTableView.add( new String[]{ localFileName, "NEW", localFileLength, localFileLastModified } );
					localNewFiles.add( new String[]{ localFileName, localFileLength, localFileLastModified } );
				}
			}

			for (String[] row : remoteFilesTableView) {
				if( row[1].equalsIgnoreCase( "MODIFIED" ) ) {
					filesToGet.add( new String[]{ row[0], row[2], row[3] } );
				}else if( row[1].equalsIgnoreCase( "NEW" ) ) {
					remoteNewFiles.add( new String[]{ row[0], row[2], row[3] } );
				}
			}

			filesToGet.addAll( remoteNewFiles );
			filesToPut.addAll( localNewFiles );

			localFilesTableView.add(0, new String[]{"local"});
			setChanged();
			notifyObservers( localFilesTableView );

			remoteFilesTableView.add(0, new String[]{"remote"});
			setChanged();
			notifyObservers( remoteFilesTableView );

			String statusMessage = "File lists updated !";
			setChanged();
			notifyObservers( statusMessage );
		}
	}

	public void update(Observable o, Object arg) {
		if( (o instanceof MirrorRemoteOnLocalTask || o instanceof MirrorLocalOnRemoteTask) ) {
			if( arg instanceof Integer ) {
				Integer progressPercentage = (Integer)arg / syncTaskCount;
				setChanged();
				notifyObservers( progressPercentage );
			}else if( arg instanceof String ){
				String string = (String) arg;
				setSyncLog(getSyncLog() + string + "\n\n") ;

				setChanged();
				notifyObservers(string);
				
				if( string.equalsIgnoreCase("SYNC_FINISHED") ) {
					getAndCompareFiles();
				}
			}
		}
	}

	public synchronized  String getSyncLog() {
		return syncLog;
	}

	public synchronized void setSyncLog(String syncLog) {
		this.syncLog = syncLog;
	}
	
}
